/**
 * @file STL_MysqlConnPool.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2020-10-14
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2020
 * 
 */

#include "STL_MysqlConnPool.h"
#include <stdlib.h>
#include "STLOG.h"
#include "STL_Json.h"

namespace STL
{

    STL_MysqlConnPool *STL_MysqlConnPool::s_instance = nullptr;
    std::string STL_MysqlConnPool::s_host = "";
    unsigned int STL_MysqlConnPool::s_port = 3306;
    std::string STL_MysqlConnPool::s_user = "";
    std::string STL_MysqlConnPool::s_password = "";
    std::string STL_MysqlConnPool::s_database = "";
    long STL_MysqlConnPool::s_maxlifetime = 3600;
    long STL_MysqlConnPool::s_idleTimeout = 300;
    int STL_MysqlConnPool::s_maxPoolSize = 0;

    std::mutex STL_MysqlConnPool::s_poolMutex;
    std::mutex STL_MysqlConnPool::s_execMutex;
    std::mutex STL_MysqlConnPool::s_initMutex;
    std::mutex STL_MysqlConnPool::s_logMutex;

    STL_MysqlConnPool::STL_MysqlConnPool()
    {
        vec = new STL_MysqlSome[STL_MysqlConnPool::s_maxPoolSize];

        for (int i = 0; i < s_maxPoolSize; i++)
        {
            STL_MysqlConnection *conn = new STL_MysqlConnection(STL_MysqlConnPool::s_host, STL_MysqlConnPool::s_port, STL_MysqlConnPool::s_user, STL_MysqlConnPool::s_password, STL_MysqlConnPool::s_database);
            if (!conn)
            {
                printf("xPool: new STL_MysqlConnection Operation failed");
            }
            vec[i].first = conn;
            vec[i].second = false;
        }
    }

    STL_MysqlConnPool::~STL_MysqlConnPool()
    {
        for (int i = 0; i < s_maxPoolSize; i++)
        {
            delete vec[i].first;
        }
        delete[] vec;
        //mysql_library_end();
    }

    int STL_MysqlConnPool::initPool(std::string host, unsigned int port, std::string user, std::string passwd, std::string database, int poolSize)
    {
        s_host = host;
        s_port = port;
        s_user = user;
        s_password = passwd;
        s_database = database;
        s_maxPoolSize = poolSize;

        return 0;
    }

    void STL_MysqlConnPool::destroyPool()
    {
        if (s_instance)
        {
            delete s_instance;
            s_instance = NULL;
        }
    }

    STL_MysqlConnection *STL_MysqlConnPool::getConnection(int index /* == -1 */)
    {
        if (index >= s_maxPoolSize) // index 不可以超过连接池个数
        {
            index = s_maxPoolSize - 1;
        }
        // init pool, open connections
        {
            std::lock_guard<std::mutex> guard(STL_MysqlConnPool::s_initMutex);
            if (s_instance == NULL)
            {
                //mysql_library_init(0,NULL,NULL);
                s_instance = new STL_MysqlConnPool();
            }
        }
        //get connection operation
        STL_MysqlConnection *ret = NULL;
        while (true)
        {
            std::lock_guard<std::mutex> guard(STL_MysqlConnPool::s_poolMutex);
            bool flag = false;
            for (int i = 0; i < s_maxPoolSize; i++)
            {
                if ((index < 0 || i == index) && s_instance->vec[i].second == false)
                {
                    s_instance->vec[i].second = true;
                    ret = s_instance->vec[i].first;
                    flag = true;
                    break;
                }
            }
            if (flag == true)
            {
                break;
            }
            else
            {
                //cout << "wait" << endl;
                usleep(1000);
                continue;
            }
        }
        return ret;
    }

    int STL_MysqlConnPool::releaseConnection(STL_MysqlConnection *conn)
    {
        std::lock_guard<std::mutex> guard(STL_MysqlConnPool::s_poolMutex);
        for (int i = 0; i < s_maxPoolSize; i++)
        {
            if (s_instance->vec[i].first == conn)
            {
                s_instance->vec[i].second = false;
                break;
            }
        }
        return 0;
    }

    std::string STL_MysqlConnPool::genSql(std::string sql, JSONCPP::Value j_array)
    {
        size_t pos = 0;
        size_t next = sql.find("?", pos);
        if (next == std::string::npos)
        {
            return sql; //不需要替换
        }
        if (!j_array.isArray())
        {
            stlog_error("array type error");
            return sql; //没有数据替换
        }
        std::string ret = "";
        for (int i = 0;; i++)
        {
            if (i >= j_array.size())
            {
                stlog_error("array Len error");
                break;
            }
            ret = ret + sql.substr(pos, next - pos);
            pos = next + 1;
            std::string value = "";
            switch (j_array[i].type())
            {
            case JSONCPP::ValueType::nullValue: ///< 'null' value
                value = "NULL";
                break;
            case JSONCPP::ValueType::intValue: ///< signed integer value
                value = std::to_string(j_array[i].asInt64());
                break;
            case JSONCPP::ValueType::uintValue: ///< unsigned integer value
                value = std::to_string(j_array[i].asUInt64());
                break;
            case JSONCPP::ValueType::realValue: ///< double value
                value = std::to_string(j_array[i].asDouble());
                break;
            case JSONCPP::ValueType::stringValue: ///< UTF-8 string value
                value = std::string("'") + j_array[i].asString() + std::string("'");
                break;
            case JSONCPP::ValueType::booleanValue: ///< bool value
                value = j_array[i].asBool() ? 1 : 0;
                break;
            case JSONCPP::ValueType::arrayValue: ///< array value (ordered list)
                value = std::string("'") + getFastString(j_array[i]) + std::string("'");
                break;
            case JSONCPP::ValueType::objectValue: ///< object value (collection of name/value pairs).
                value = std::string("'") + getFastString(j_array[i]) + std::string("'");
                break;
            default:
                stlog_error("jsoncpp type error");
                break;
            }
            ret = ret + value;
            next = sql.find("?", pos);
            if (next == std::string::npos)
            {
                if (i + 1 != j_array.size())
                {
                    stlog_warn("jsoncpp len warn");
                }
                break;
            }
        }

        ret = ret + sql.substr(pos);
        return ret;
    }
} // namespace STL
